package com.xydtech.interceptor;

import com.xydtech.common.annotation.PassToken;
import com.xydtech.common.cache.EhcacheUtil;
import com.xydtech.common.enums.ResultStatus;
import com.xydtech.common.exception.ApiException;
import com.xydtech.common.model.TokenModel;
import com.xydtech.common.token.JwtTokenUtil;
import com.xydtech.common.utils.spring.SpringUtils;
import com.xydtech.common.utils.string.StringUtil;
import com.xydtech.constant.EcardApiConstants;
import com.xydtech.context.EcardApiBaseContextHandler;
import org.apache.shiro.cache.CacheManager;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;


/**
 * @author jinbin
 * @date 2018-07-08 20:41
 */
@Component
public class EcardAuthenticationInterceptor extends HandlerInterceptorAdapter {

    private static org.apache.shiro.cache.CacheManager cacheManager = SpringUtils.getBean(CacheManager.class);

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //如果不是映射到方法直接通过
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }

        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();

        String authorization = request.getHeader(EcardApiConstants.AUTHORIZATION);

        String platform = request.getHeader(EcardApiConstants.PLATFORM);
        EcardApiBaseContextHandler.setPlatform(platform);

        //检查是否有passtoken注释，有则跳过认证
        if (method.isAnnotationPresent(PassToken.class)) {
            PassToken passToken = method.getAnnotation(PassToken.class);
            if (passToken.required()) {
                return super.preHandle(request, response, handler);
            }
        }

        //验证token
        TokenModel model = null;
        try {
            model = getToken(authorization);
        } catch (ApiException ex) {
            throw new ApiException(ex.getStatus());
        }
        if (model != null) {
            EcardApiBaseContextHandler.setCustomerId(model.getUserId());
            return super.preHandle(request, response, handler);
        }
        throw new ApiException(ResultStatus.TOKEN_EXP);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        EcardApiBaseContextHandler.remove();
        super.afterCompletion(request, response, handler, ex);
    }

    private TokenModel getToken(String authentication) {
        try {
            if (StringUtil.isNullOrEmpty(authentication)) {
                return null;
            }
            if (!JwtTokenUtil.parseTokenT(authentication)) {
                throw new ApiException(ResultStatus.TOKEN_INVALID);
            }
            if (JwtTokenUtil.isTokenExpired(authentication)) {
                throw new ApiException(ResultStatus.TOKEN_EXP);
            }
            if (JwtTokenUtil.getExpirationDateFromToken(authentication).getTime() < System.currentTimeMillis()) {
                throw new ApiException(ResultStatus.TOKEN_EXP);
            }
            String userId = JwtTokenUtil.getUserIdFromToken(authentication);
            String cookieKey = EcardApiConstants.JWT_KEY_USER_ID + userId;

            String cookieValue = EhcacheUtil.getEcardAccessToken(cookieKey, cacheManager);
            if (StringUtil.isNullOrEmpty(cookieValue)) {
                throw new ApiException(ResultStatus.TOKEN_INVALID);
            }

            if (!cookieValue.equals(authentication)) {
                throw new ApiException(ResultStatus.TOKEN_INVALID);
            }

            return new TokenModel(Integer.parseInt(userId), authentication);
        } catch (ApiException ex) {
            throw new ApiException(ex.getStatus());
        } catch (Exception ex) {
            return null;
        }
    }
}
